/**
 * Copyright (c) 2022 Presto Labs Pte. Ltd.
 * Author: Ju
 */

#pragma once

#include <boost/asio.hpp>
#include <functional>
#include <iostream>
#include <nlohmann/json.hpp>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>

#include "coin2/base/packer.h"
#include "coin2/base/unpacker.h"
#include "coin2/controller/base_type.h"
#include "coin2/controller/message_assembler.h"
#include "coin2/controller/tcp_client.h"
namespace coin2::controller {
using namespace coin2::base;
using Task = std::function<void(uint32_t, nlohmann::json)>;
using MethodsVector = std::vector<std::pair<
    std::string,
    std::map<std::string, std::function<std::string(const std::string&)>>>>;

class Controller : MessageAssembler::Callback {
 public:
  explicit Controller(boost::asio::io_context* ioc, std::string strategy_name);
  ~Controller();

  void ExportMethods(const MethodsVector& v);
  template <typename F>
  bool Register(const std::string& key, F&& f) {
    Task task;
    task = [this, func = std::move(f)](uint32_t dst_id,
                                       nlohmann::json payload) -> void {
      auto params =
          payload["Params"].get<std::unordered_map<std::string, std::string>>();
      std::string data = func(params["request"]);
      payload["Result"]["data"] = data;
      tcp_client_->SendMsg(MakeResponse(node_id_, dst_id, payload));
    };
    return tasks.try_emplace(key, task).second;
  }

  void RegisterToAgent();

  bool Find(const std::string& str);
  void Execute(uint32_t dst_id, nlohmann::json payload);
  void Connect();

  void OnTimer();

 private:
  // inherit form  MessageAssembler::Callback
  void OnProcessMessage(memory::MemSlice data) override;
  void OnBroken() override;

 private:
  boost::asio::io_context* ioc_;
  std::string strategy_name_;
  std::string host_name_;
  uint32_t node_id_;
  int64_t pid_;
  int64_t req_num_{0};
  std::unique_ptr<TcpClient> tcp_client_;
  std::unordered_map<std::string, Task> tasks;
  boost::asio::deadline_timer timer_;
};

}  // namespace coin2::controller